/************************************************************
Sockets.c

A slightly modified version of openfiles/opensocks by Stewart Brodie
************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
//#include <errno.h>

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/errno.h>
/* #include "sys/byteorder.h" */
#include <arpa/inet.h>
#include <netdb.h>
// is this the right one?
/*#include "bind:dnsquery.h" */
#include <kernel.h>

#define Director_Menu		0x4A100
#define Director_EndMenu	0x4A101
#define Director_Option		0x4A102
#define Director_Dash		0x4A103
#define Director_Command	0x4A104
#define Director_Enumerate	0x4A105
#define Director_Memory		0x4A106
#define Director_QuickSort	0x4A107
#define Director_SortMenu	0x4A108

static void port(char *buffer, int port2, int sock)
{
    struct servent *s;
    char *proto;
    char buf[16];
    int len = 16;
    int res;

    res = getsockopt(sock, SOL_SOCKET, SO_TYPE, buf, &len);
    if (res != -1)
    {
        switch (*buf)
        {
        case SOCK_STREAM:
            proto = "tcp";
            break;
        case SOCK_DGRAM:
            proto = "udp";
            break;
        case SOCK_RAW:
            proto = "icmp";
            break;
        default:
            proto = "";
            break;
        }
    }
    else
        proto = "";

    s = getservbyport(port2, (*proto) ? proto : NULL);

    if (s)
    {
        strcat(buffer, s->s_name);
        strcat(buffer, "/");
        strcat(buffer, proto);
    }
    else
    {
        sprintf(buf, "%d/%s", ntohs(port2), proto);
        strcat(buffer, buf);
    }
}

static char *ip(int s, struct sockaddr_in *sin)
{
    static char buffer[256];
/*         struct hostent *host; */

    strcpy(buffer, inet_ntoa(sin->sin_addr));
    if (sin->sin_addr.s_addr == INADDR_ANY)
    {
        strcpy(buffer, "Server");
    }
    else if ((sin->sin_addr.s_addr & 0xff) == 0x7f)
    {
        strcpy(buffer, "Local");
    }
    else
    {
        strcpy(buffer, inet_ntoa(sin->sin_addr));
/*
        dnsquery *q = dnsquery_gethostbyaddr((char *) &sin->sin_addr, 4, AF_INET);
        if (q)
        {
            dns_status s;
            do
            {
                s = dnsquery_check(q);
            }
            while (s != dns_query_complete_success &&
                   s != dns_query_complete_failure);
            if (s == dns_query_complete_success)
            {
                host = dnsquery_getanswer(q);
                if (host)
                {
                    buffer[0] = '\0';
                    strncat(buffer, host->h_name, 200);
                }
            }
            dnsquery_dispose(q);
        }
*/
    }

    strcat(buffer, " (");
    port(buffer, sin->sin_port, s);
    return strcat(buffer, ")");
}

static int opensocks(char *flags)
{
    struct sockaddr_in sin;
    int size;
    int s;
/*  extern int gettsize(void); */
    int maxs = /*gettsize() */ 32;
    const int noexist = !!strchr(flags, 'a');
    const int noerror = !!strchr(flags, 'e');
    const int director = !!strchr(flags, 'd');
    char line[256];
    char *pline;
    _kernel_swi_regs r;

    if (director)
    {
        _kernel_oscli("Set Director$Menu Open Sockets");
        r.r[0] = (int) "\"<Director$Menu>\" -temp";
        _kernel_swi(Director_Menu, &r, &r);
    }

    for (s = 0; s < maxs; ++s)
    {
        pline = line;
        if (director)
            *pline++ = '"';
        size = 16;
        errno = 0;
        if (getsockname(s, (struct sockaddr *) &sin, &size) < 0)
        {
            if (((errno == EBADF) && noexist) ||
                (errno != EBADF) && noerror)
            {
                pline += sprintf(pline, "%8d (errno %d)\n", s, errno);
            }
            continue;
        }

        pline += sprintf(pline, "%8d %s", s, ip(s, &sin));
        size = 16;
        if (getpeername(s, (struct sockaddr *) &sin, &size) > -1)
        {
            pline += sprintf(pline, " to %s", ip(s, &sin));
        }

        if (director)
        {
            pline += sprintf(pline, "\" -sub \"Dynamic:/Director:Menus.System.SocketsSub %i\"", s);
            r.r[0] = (int) line;
            _kernel_swi(Director_Option, &r, &r);
        }
        else
        {
            puts(line);
        }
    }

    if (director)
    {
        r.r[0] = (int) "";
        _kernel_swi(Director_EndMenu, &r, &r);
    }


    return 0;
}

int main(int argc, char *argv[])
{
    argc = argc;
    return opensocks(argv[1]);
}
